using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using SharpCompress.Archives;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Readers;
using System.Collections.ObjectModel;
using WinForms = System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using System.Reflection;

namespace WinFormsApp3
{
    public class FileItem
    {
        public string Name { get; set; } = "";
        public string Type { get; set; } = "";
        public string Size { get; set; } = "";
        public string ModifiedTime { get; set; } = "";
        public object Tag { get; set; }
        public ImageSource Icon { get; set; }
    }

    public partial class MainWindow : Window
    {
        private IArchive? currentArchive;
        private string currentArchivePath = "";
        private string currentDirectory = "";
        private MemoryStream? tarArchiveStream;
        private ObservableCollection<FileItem> fileItems = new();
        private ImageSource folderIcon;
        private ImageSource fileIcon;

        public MainWindow() : this(Environment.GetCommandLineArgs().Length > 1 ? Environment.GetCommandLineArgs()[1] : "") { }

        public MainWindow(string archivePath)
        {
            InitializeComponent();
            listViewFiles.ItemsSource = fileItems;
            folderIcon = GetIcon("folder", true) ?? new BitmapImage(new Uri("pack://application:,,,/Resources/folder.png"));
            fileIcon = GetIcon("file.txt", false) ?? new BitmapImage(new Uri("pack://application:,,,/Resources/file.png"));
            
            // Check if file associations are registered and update menu item
            if (menuFileAssociation != null)
            {
                menuFileAssociation.IsChecked = FileAssociationHelper.AreFileAssociationsRegistered();
            }
            
            if (!string.IsNullOrEmpty(archivePath))
            {
                OpenArchive(archivePath);
            }
        }

        private void OpenArchive(string archivePath)
        {
            currentArchivePath = archivePath;
            if (archivePath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase) || archivePath.EndsWith(".tgz", StringComparison.OrdinalIgnoreCase))
            {
                using (var gzStream = File.OpenRead(archivePath))
                using (var gzArchive = SharpCompress.Archives.GZip.GZipArchive.Open(gzStream))
                {
                    var gzEntry = gzArchive.Entries.FirstOrDefault(e => !e.IsDirectory);
                    if (gzEntry != null)
                    {
                        tarArchiveStream = new MemoryStream();
                        using (var entryStream = gzEntry.OpenEntryStream())
                        {
                            entryStream.CopyTo(tarArchiveStream);
                            tarArchiveStream.Position = 0;
                            currentArchive = TarArchive.Open(tarArchiveStream);
                            treeViewArchive.Items.Clear();
                            var root = CreateTreeViewItem(Path.GetFileName(archivePath), true, "");
                            BuildTree(root, currentArchive.Entries.Select(e => e.Key));
                            treeViewArchive.Items.Add(root);
                            root.IsExpanded = true;
                            root.IsSelected = true;
                            currentDirectory = "";
                            ShowCurrentDirectoryFiles();
                        }
                    }
                }
            }
            else
            {
                currentArchive = ArchiveFactory.Open(archivePath);
                treeViewArchive.Items.Clear();
                var root = CreateTreeViewItem(Path.GetFileName(archivePath), true, "");
                BuildTree(root, currentArchive.Entries.Select(e => e.Key));
                treeViewArchive.Items.Add(root);
                root.IsExpanded = true;
                root.IsSelected = true;
                currentDirectory = "";
                ShowCurrentDirectoryFiles();
            }
        }

        private void BtnOpenArchive_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.Filter = "All Supported Archives (*.zip;*.rar;*.7z;*.tar;*.gz;*.tar.gz;*.tgz)|*.zip;*.rar;*.7z;*.tar;*.gz;*.tar.gz;*.tgz|ZIP (*.zip)|*.zip|RAR (*.rar)|*.rar|7z (*.7z)|*.7z|TAR (*.tar)|*.tar|GZ (*.gz)|*.gz|TAR.GZ (*.tar.gz;*.tgz)|*.tar.gz;*.tgz";
            if (dlg.ShowDialog() == true)
            {
                currentArchivePath = dlg.FileName;
                if (currentArchivePath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase) || currentArchivePath.EndsWith(".tgz", StringComparison.OrdinalIgnoreCase))
                {
                    using (var gzStream = File.OpenRead(currentArchivePath))
                    using (var gzArchive = SharpCompress.Archives.GZip.GZipArchive.Open(gzStream))
                    {
                        var gzEntry = gzArchive.Entries.FirstOrDefault(e => !e.IsDirectory);
                        if (gzEntry != null)
                        {
                            tarArchiveStream = new MemoryStream();
                            using (var entryStream = gzEntry.OpenEntryStream())
                            {
                                entryStream.CopyTo(tarArchiveStream);
                                tarArchiveStream.Position = 0;
                                currentArchive = TarArchive.Open(tarArchiveStream);
                                treeViewArchive.Items.Clear();
                                var root = CreateTreeViewItem(Path.GetFileName(currentArchivePath), true, "");
                                BuildTree(root, currentArchive.Entries.Select(e => e.Key));
                                treeViewArchive.Items.Add(root);
                                root.IsExpanded = true;
                                root.IsSelected = true;
                                currentDirectory = "";
                                ShowCurrentDirectoryFiles();
                            }
                        }
                    }
                }
                else
                {
                    currentArchive = ArchiveFactory.Open(currentArchivePath);
                    treeViewArchive.Items.Clear();
                    var root = CreateTreeViewItem(Path.GetFileName(currentArchivePath), true, "");
                    BuildTree(root, currentArchive.Entries.Select(e => e.Key));
                    treeViewArchive.Items.Add(root);
                    root.IsExpanded = true;
                    root.IsSelected = true;
                    currentDirectory = "";
                    ShowCurrentDirectoryFiles();
                }
            }
        }

        private void BuildTree(TreeViewItem root, IEnumerable<string> entries)
        {
            var dirSet = new HashSet<string>();
            // 先处理所有目录条目（包括空文件夹）
            if (currentArchive != null)
            {
                foreach (var entry in currentArchive.Entries)
                {
                    if (entry.IsDirectory && !string.IsNullOrEmpty(entry.Key))
                    {
                        var parts = entry.Key.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
                        if (parts.Length > 0)
                        {
                            string currentPath = string.Join("/", parts);
                            if (dirSet.Add(currentPath))
                            {
                                AddDirToTree(root, currentPath);
                            }
                        }
                    }
                }
            }
            // 再处理原有的文件路径逻辑，保证所有父目录都能加入
            foreach (var entryPath in entries)
            {
                if (string.IsNullOrEmpty(entryPath)) continue;
                var parts = entryPath.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length <= 1) continue;
                string currentPath = "";
                for (int i = 0; i < parts.Length - 1; i++)
                {
                    currentPath = string.IsNullOrEmpty(currentPath) ? parts[i] : currentPath + "/" + parts[i];
                    if (dirSet.Add(currentPath))
                    {
                        AddDirToTree(root, currentPath);
                    }
                }
            }
        }

        private TreeViewItem CreateTreeViewItem(string name, bool isFolder, string tag)
        {
            var sp = new System.Windows.Controls.StackPanel { Orientation = System.Windows.Controls.Orientation.Horizontal };
            var img = new System.Windows.Controls.Image { Source = isFolder ? folderIcon : fileIcon, Width = 16, Height = 16, Margin = new System.Windows.Thickness(0, 0, 5, 0) };
            var txt = new System.Windows.Controls.TextBlock { Text = name, VerticalAlignment = System.Windows.VerticalAlignment.Center };
            sp.Children.Add(img);
            sp.Children.Add(txt);
            return new TreeViewItem { Header = sp, Tag = tag };
        }

        private void AddDirToTree(TreeViewItem root, string dirPath)
        {
            var parts = dirPath.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            TreeViewItem node = root;
            foreach (var part in parts)
            {
                var found = node.Items.OfType<TreeViewItem>().FirstOrDefault(n =>
                {
                    if (n.Header is System.Windows.Controls.StackPanel sp && sp.Children.Count > 1 && sp.Children[1] is System.Windows.Controls.TextBlock tb)
                        return tb.Text == part;
                    return n.Header?.ToString() == part;
                });
                if (found == null)
                {
                    found = CreateTreeViewItem(part, true, string.Join("/", parts.TakeWhile(p => p != part).Concat(new[] { part })));
                    node.Items.Add(found);
                }
                node = found;
            }
        }

        private void TreeViewArchive_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (treeViewArchive.SelectedItem is TreeViewItem node)
            {
                currentDirectory = GetFullPathFromNode(node);
                ShowCurrentDirectoryFiles();
            }
        }

        private void BtnBack_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(currentDirectory)) return;
            var parts = currentDirectory.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (parts.Length > 0) currentDirectory = string.Join("/", parts.Take(parts.Length - 1));
            ShowCurrentDirectoryFiles();
            SelectTreeNodeByPath(currentDirectory);
        }

        private void SelectTreeNodeByPath(string path)
        {
            TreeViewItem node = treeViewArchive.Items.Count > 0 ? treeViewArchive.Items[0] as TreeViewItem : null;
            if (node == null) return;
            if (string.IsNullOrEmpty(path)) return;
            var parts = path.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var part in parts)
            {
                node = node.Items.OfType<TreeViewItem>().FirstOrDefault(n => n.Header.ToString() == part);
                if (node == null) return;
            }
            node.IsSelected = true;
        }

        private void ShowCurrentDirectoryFiles()
        {
            fileItems.Clear();
            if (currentArchive == null) return;
            // 添加返回上级目录的“..”项
            if (!string.IsNullOrEmpty(currentDirectory))
            {
                fileItems.Add(new FileItem
                {
                    Name = "..",
                    Type = "Folder",
                    Size = "-",
                    ModifiedTime = "-",
                    Tag = "..",
                    Icon = folderIcon
                });
            }
            var entries = currentArchive.Entries.ToList();
            var dirs = new HashSet<string>();
            string prefix = string.IsNullOrEmpty(currentDirectory) ? "" : currentDirectory + "/";

            // 统计所有当前目录下的文件夹（包括空文件夹）
            foreach (var entry in entries)
            {
                if (string.IsNullOrEmpty(entry.Key)) continue;
                if (!entry.Key.StartsWith(prefix)) continue;
                var subPath = entry.Key.Substring(prefix.Length);
                var subParts = subPath.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
                if (subParts.Length > 0)
                {
                    // 如果是当前目录下的文件夹（包括空文件夹）
                    if (subParts.Length == 1 && entry.IsDirectory)
                    {
                        var dirName = subParts[0];
                        if (!dirs.Contains(dirName))
                        {
                            fileItems.Add(new FileItem
                            {
                                Name = dirName,
                                Type = "Folder",
                                Size = "-",
                                ModifiedTime = entry.LastModifiedTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-",
                                Tag = entry,
                                Icon = GetIcon(dirName, true)
                            });
                            dirs.Add(dirName);
                        }
                    }
                    // 如果是当前目录下的子文件夹（非空文件夹）
                    else if (subParts.Length > 1)
                    {
                        var dirName = subParts[0];
                        if (!dirs.Contains(dirName))
                        {
                            fileItems.Add(new FileItem
                            {
                                Name = dirName,
                                Type = "Folder",
                                Size = "-",
                                ModifiedTime = "-",
                                Tag = dirName,
                                Icon = GetIcon(dirName, true)
                            });
                            dirs.Add(dirName);
                        }
                    }
                    // 如果是当前目录下的文件
                    else if (subParts.Length == 1 && !entry.IsDirectory)
                    {
                        fileItems.Add(new FileItem
                        {
                            Name = subParts[0],
                            Type = "File",
                            Size = entry.Size.ToString(),
                            ModifiedTime = entry.LastModifiedTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-",
                            Tag = entry,
                            Icon = GetIcon(subParts[0], false)
                        });
                    }
                }
            }
        }

        private bool IsArchiveFile(string fileName)
        {
            string[] exts = { ".zip", ".rar", ".7z", ".tar", ".gz", ".tar.gz", ".tgz" };
            return exts.Any(ext => fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
        }
        
        private void BtnSuperExtract_Click(object sender, RoutedEventArgs e)
        {
            if (currentArchive == null) return;
            if (treeViewArchive.SelectedItem is not TreeViewItem node) return;
            
            var dlg = new WinForms.FolderBrowserDialog();
            if (dlg.ShowDialog() == WinForms.DialogResult.OK)
            {
                string destDir = dlg.SelectedPath;
                string selectedPath = GetFullPathFromNode(node);
                
                // Extract the selected archive first
                ExtractArchiveRecursively(currentArchive, selectedPath, destDir);
                
                WinForms.MessageBox.Show("Super extraction completed!");
            }
        }
        
        // 是否包含空文件夹，默认不包含
        private bool IncludeEmptyFolders => chkIncludeEmptyFolders?.IsChecked == true;

        private void ExtractArchiveRecursively(IArchive archive, string selectedPath, string destDir)
        {
            foreach (var entry in archive.Entries)
            {
                if (entry.Key.StartsWith(selectedPath))
                {
                    // 如果是空文件夹且不包含空文件夹选项未勾选，则跳过
                    if (entry.IsDirectory && !IncludeEmptyFolders)
                    {
                        string folderPrefix = entry.Key.TrimEnd(new char[] { '/', '\\' }) + "/";
                        bool hasChild = archive.Entries.Any(e => e.Key.StartsWith(folderPrefix) && e.Key != entry.Key);
                        if (!hasChild)
                            continue;
                    }
                    string outPath = Path.Combine(destDir, entry.Key);
                    Directory.CreateDirectory(Path.GetDirectoryName(outPath)!);
                    if (!entry.IsDirectory)
                    {
                        using (var stream = File.OpenWrite(outPath))
                        {
                            entry.WriteTo(stream);
                        }
                        if (IsArchiveFile(outPath))
                        {
                            try
                            {
                                string nestedDestDir = Path.Combine(
                                    Path.GetDirectoryName(outPath)!,
                                    Path.GetFileNameWithoutExtension(outPath)
                                );
                                Directory.CreateDirectory(nestedDestDir);
                                if (outPath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase) || 
                                    outPath.EndsWith(".tgz", StringComparison.OrdinalIgnoreCase))
                                {
                                    using (var gzStream = File.OpenRead(outPath))
                                    using (var gzArchive = SharpCompress.Archives.GZip.GZipArchive.Open(gzStream))
                                    {
                                        var gzEntry = gzArchive.Entries.FirstOrDefault(e => !e.IsDirectory);
                                        if (gzEntry != null)
                                        {
                                            using (var tarStream = new MemoryStream())
                                            {
                                                using (var entryStream = gzEntry.OpenEntryStream())
                                                {
                                                    entryStream.CopyTo(tarStream);
                                                }
                                                tarStream.Position = 0;
                                                using (var tarArchive = TarArchive.Open(tarStream))
                                                {
                                                    ExtractArchiveRecursively(tarArchive, "", nestedDestDir);
                                                }
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    using (var nestedArchive = ArchiveFactory.Open(outPath))
                                    {
                                        ExtractArchiveRecursively(nestedArchive, "", nestedDestDir);
                                    }
                                }
                                try
                                {
                                    GC.Collect();
                                    GC.WaitForPendingFinalizers();
                                    File.Delete(outPath);
                                }
                                catch (Exception ex)
                                {
                                    Debug.WriteLine($"Failed to delete archive file {outPath}: {ex.Message}");
                                }
                            }
                            catch (Exception ex)
                            {
                                Debug.WriteLine($"Failed to extract nested archive {outPath}: {ex.Message}");
                            }
                        }
                    }
                }
            }
        }

        private void ListViewFiles_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (listViewFiles.SelectedItem is not FileItem item) return;
            if (item.Type == "Folder")
            {
                if (item.Name == "..")
                {
                    // ������һ��
                    var parts = currentDirectory.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
                    if (parts.Length > 0)
                        currentDirectory = string.Join("/", parts.Take(parts.Length - 1));
                    else
                        currentDirectory = "";
                    ShowCurrentDirectoryFiles();
                    SelectTreeNodeByPath(currentDirectory);
                }
                else
                {
                    currentDirectory = string.IsNullOrEmpty(currentDirectory) ? item.Name : currentDirectory + "/" + item.Name;
                    ShowCurrentDirectoryFiles();
                    SelectTreeNodeByPath(currentDirectory);
                }
            }
            else if (item.Tag is IArchiveEntry entry)
            {
                string tempPath = Path.Combine(Path.GetTempPath(), entry.Key);
                Directory.CreateDirectory(Path.GetDirectoryName(tempPath)!);
                using (var stream = File.OpenWrite(tempPath))
                {
                    entry.WriteTo(stream);
                }
                if (IsArchiveFile(tempPath))
                {
                    var win = new MainWindow(tempPath);
                    win.Show();
                }
                else
                {
                    Process.Start(new ProcessStartInfo(tempPath) { UseShellExecute = true });
                }
            }
        }

        private void BtnExtract_Click(object sender, RoutedEventArgs e)
        {
            if (currentArchive == null) return;
            if (treeViewArchive.SelectedItem is not TreeViewItem node) return;
            var dlg = new WinForms.FolderBrowserDialog();
            if (dlg.ShowDialog() == WinForms.DialogResult.OK)
            {
                string destDir = dlg.SelectedPath;
                string selectedPath = GetFullPathFromNode(node);
                foreach (var entry in currentArchive.Entries)
                {
                    if (entry.Key.StartsWith(selectedPath))
                    {
                        // 如果是空文件夹且不包含空文件夹选项未勾选，则跳过
                        if (entry.IsDirectory && !IncludeEmptyFolders)
                        {
                            // 判断是否为真正的空文件夹（没有任何子项）
                            string folderPrefix = entry.Key.TrimEnd(new char[] { '/', '\\' }) + "/";
                            bool hasChild = currentArchive.Entries.Any(e => e.Key.StartsWith(folderPrefix) && e.Key != entry.Key);
                            if (!hasChild)
                                continue;
                        }
                        string outPath = Path.Combine(destDir, entry.Key);
                        Directory.CreateDirectory(Path.GetDirectoryName(outPath)!);
                        if (!entry.IsDirectory)
                        {
                            using (var stream = File.OpenWrite(outPath))
                            {
                                entry.WriteTo(stream);
                            }
                        }
                    }
                }
                WinForms.MessageBox.Show("Extract completed!");
            }
        }

        private string GetFullPathFromNode(TreeViewItem node)
        {
            var parts = new List<string>();
            var n = node;
            while (n != null)
            {
                if (n.Header is System.Windows.Controls.StackPanel sp && sp.Children.Count > 1 && sp.Children[1] is System.Windows.Controls.TextBlock tb)
                    parts.Insert(0, tb.Text);
                else if (n.Header is string s)
                    parts.Insert(0, s);
                if (n.Parent is TreeViewItem parent)
                    n = parent;
                else
                    break;
            }
            // Remove the root archive name from the path
            if (parts.Count > 0) parts.RemoveAt(0);
            return string.Join("/", parts);
        }

        private IEnumerable<string> GetNodeParts(TreeViewItem node)
        {
            var parts = new List<string>();
            var n = node;
            while (n.Parent is TreeViewItem parent)
            {
                if (parent.Header is System.Windows.Controls.StackPanel sp && sp.Children.Count > 1 && sp.Children[1] is System.Windows.Controls.TextBlock tb)
                    parts.Insert(0, tb.Text);
                else
                    parts.Insert(0, n.Header.ToString());
                n = parent;
            }
            return parts;
        }

        // --------- Shell icon retrieval using SHGetFileInfo ----------
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct SHFILEINFO
        {
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint uFlags);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool DestroyIcon(IntPtr hIcon);

        private const uint SHGFI_ICON = 0x000000100;
        private const uint SHGFI_SMALLICON = 0x000000001;
        private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
        private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
        private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
        
        // Menu event handlers
        private void MenuExit_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void MenuFileAssociation_Click(object sender, RoutedEventArgs e)
        {
            if (menuFileAssociation.IsChecked)
            {
                FileAssociationHelper.RegisterFileAssociations();
            }
            else
            {
                FileAssociationHelper.RemoveFileAssociations();
            }
        }


        private ImageSource GetIcon(string name, bool isFolder)
        {
            try
            {
                uint flags = SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES;
                uint attr = isFolder ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
                SHFILEINFO shfi = new SHFILEINFO();
                IntPtr result = SHGetFileInfo(name, attr, out shfi, (uint)Marshal.SizeOf<SHFILEINFO>(), flags);
                if (result == IntPtr.Zero || shfi.hIcon == IntPtr.Zero) return null;
                var bmp = Imaging.CreateBitmapSourceFromHIcon(shfi.hIcon, Int32Rect.Empty, BitmapSizeOptions.FromWidthAndHeight(16, 16));
                DestroyIcon(shfi.hIcon);
                bmp.Freeze();
                return bmp;
            }
            catch
            {
                return null;
            }
        }
    }
}